﻿using System;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Web;
namespace System.Web.Hosting
{
    internal class MemoryBytes
    {
        private byte[] _arrayData;
        private BufferType _bufferType;
        private IntPtr _fileHandle;
        private string _fileName;
        private long _fileSize;
        private IntPtr _intptrData;
        private long _offset;
        private GCHandle _pinnedArrayData;
        private int _size;

        internal MemoryBytes(byte[] data, int size)
            : this(data, size, false, 0L) { }
        internal MemoryBytes(IntPtr data, int size, BufferType bufferType)
        {
            _size = size;
            _arrayData = null;
            _intptrData = data;
            _fileHandle = IntPtr.Zero;
            _bufferType = bufferType;
        }
        internal MemoryBytes(string fileName, long offset, long fileSize)
        {
            _bufferType = System.Web.Hosting.BufferType.TransmitFile;
            _intptrData = IntPtr.Zero;
            _fileHandle = IntPtr.Zero;
            _fileSize = fileSize;
            _fileName = fileName;
            _offset = offset;
            _size = IntPtr.Size;
        }
        internal MemoryBytes(byte[] data, int size, bool useTransmitFile, long fileSize)
        {
            _size = size;
            _arrayData = data;
            _intptrData = IntPtr.Zero;
            _fileHandle = IntPtr.Zero;
            if (useTransmitFile)
                _bufferType = BufferType.TransmitFile;
            _fileSize = fileSize;
        }

        private void CloseHandle()
        {
            if ((_fileHandle != IntPtr.Zero) && (_fileHandle != UnsafeNativeMethods.INVALID_HANDLE_VALUE))
            {
                UnsafeNativeMethods.CloseHandle(_fileHandle);
                _fileHandle = IntPtr.Zero;
            }
        }

        private static byte[] IntPtrToBytes(IntPtr p, long offset, long length)
        {
            var buffer = new byte[0x10 + IntPtr.Size];
            for (int i = 0; i < 8; i++)
                buffer[i] = (byte)((offset >> (8 * i)) & 0xffL);
            for (int j = 0; j < 8; j++)
                buffer[8 + j] = (byte)((length >> (8 * j)) & 0xffL);
            if (IntPtr.Size == 4)
            {
                int num3 = p.ToInt32();
                for (int m = 0; m < 4; m++)
                    buffer[0x10 + m] = (byte)((num3 >> (8 * m)) & 0xff);
                return buffer;
            }
            long num5 = p.ToInt64();
            for (int k = 0; k < 8; k++)
                buffer[0x10 + k] = (byte)((num5 >> (8 * k)) & 0xffL);
            return buffer;
        }

        internal IntPtr LockMemory()
        {
            SetHandle();
            if (_arrayData != null)
            {
                _pinnedArrayData = GCHandle.Alloc(_arrayData, GCHandleType.Pinned);
                return Marshal.UnsafeAddrOfPinnedArrayElement(_arrayData, 0);
            }
            return _intptrData;
        }

        private void SetHandle()
        {
            if (_fileName != null)
                _fileHandle = UnsafeNativeMethods.GetFileHandleForTransmitFile(_fileName);
            if (_fileHandle != IntPtr.Zero)
                _arrayData = IntPtrToBytes(_fileHandle, _offset, _fileSize);
        }

        internal void UnlockMemory()
        {
            CloseHandle();
            if (_arrayData != null)
                _pinnedArrayData.Free();
        }

        internal BufferType BufferType
        {
            get { return _bufferType; }
        }

        internal long FileSize
        {
            get { return _fileSize; }
        }

        internal bool IsBufferFromUnmanagedPool
        {
            get { return (_bufferType == BufferType.UnmanagedPool); }
        }

        internal int Size
        {
            get { return _size; }
        }

        internal bool UseTransmitFile
        {
            get { return (_bufferType == BufferType.TransmitFile); }
        }
    }
}
